package image.color;

import math.mathExtended.MathExtended;

/**
 * Stores the R, G, B values of a color as floats, 
 * used when accuracy to decimal places is required. 
 * Storage per Color_Float is 96-bits (3 times 32-bits)
 * 
 * @author Shimu
 *
 */
public class Color_Float implements Color<Color_Float> {

    private float R, G, B;

    public final static Color_Float WHITE, BLACK, RED, GREEN, BLUE, YELLOW,
            ORANGE, BROWN, TURQUOISE, PINK, PURPLE, GOLD, SILVER;

    public static Color_Float max = new Color_Float();

    public static Color_Float min = new Color_Float();

    static {
        WHITE = new Color_Float(255, 255, 255);
        BLACK = new Color_Float(0, 0, 0);
        RED = new Color_Float(255, 0, 0);
        GREEN = new Color_Float(0, 255, 0);
        BLUE = new Color_Float(0, 0, 255);
        //GREEN = new Color_Float(34, 139, 34);
        YELLOW = new Color_Float(255, 255, 0);
        ORANGE = new Color_Float(255, 140, 0);
        BROWN = new Color_Float(165, 42, 42);
        TURQUOISE = new Color_Float(0, 206, 209);
        PINK = new Color_Float(255, 105, 180);
        PURPLE = new Color_Float(160, 32, 240);

        GOLD = new Color_Float(255, 215, 0);
        SILVER = new Color_Float(192, 192, 192);
    }

    // protected boolean edge, connected;
    public float getR() {
        return R;
    }

    public float getG() {
        return G;
    }

    public float getB() {
        return B;
    }

    public Color_Float(int intRep) {
        set(intRep);
    }

    public Color_Float() {
        this(0, 0, 0);
    }

    public Color_Float(int R, int G, int B) {
        this((float) R, (float) G, (float) B);
    }

    public Color_Float(double R, double G, double B) {
        this((float) R, (float) G, (float) B);
    }

    public Color_Float(Color_Float color) {
        this(color.R, color.G, color.B);
    }

    public Color_Float(java.awt.Color color) {
        this(color.getRGB());
    }

    public Color_Float(float R, float G, float B) {
        this.R = R;
        this.G = G;
        this.B = B;
    }

    @Override
    public void add(Color_Float c) {
        R += c.R;
        G += c.G;
        B += c.B;
    }

    public void add(float R, float G, float B) {
        this.R += R;
        this.G += G;
        this.B += B;
    }

    public static Color_Float add(Color_Float c1, Color_Float c2) {
        return new Color_Float(c1.R + c2.R, c1.G + c2.G, c1.B + c2.B);
    }

    public final void set(int intRep) {
        R = (intRep >> 16) & 255;
        G = (intRep >> 8) & 255;
        B = intRep & 255;
    }

    @Override
    public void subtract(Color_Float c) {
        this.R -= c.R;
        this.G -= c.G;
        this.B -= c.B;
    }

    public void subtract(float R, float G, float B) {
        this.R -= R;
        this.G -= G;
        this.B -= B;
    }

    @Override
    public void scale(double scalar) {
        R *= scalar;
        G *= scalar;
        B *= scalar;
    }

    public static Color_Float subtract(Color_Float c1, Color_Float c2) {
        return new Color_Float(c1.R - c2.R, c1.G - c2.G, c1.B - c2.B);
    }

    public static Color_Float scale(Color_Float c, float scalar) {
        return new Color_Float(c.R * scalar, c.G * scalar, c.B * scalar);
    }

    public static Color_Float max(Color_Float c1, Color_Float c2) {
        return new Color_Float(Math.max(c1.R, c2.R), Math.max(c1.G, c2.G),
                Math.max(c1.B, c2.B));
    }

    public static Color_Float min(Color_Float c1, Color_Float c2) {
        return new Color_Float(Math.min(c1.R, c2.R), Math.min(c1.G, c2.G),
                Math.min(c1.B, c2.B));
    }

    @Override
    public int intRep() {
        int alpha = 255;
        return (alpha << 24) | ((int) R << 16) | ((int) G << 8) | ((int) B);
    }

    public static int intRep(double R, double G, double B) {
        return (65536 * (int) R + 256 * (int) G + (int) B);
    }

    public int toGradient() {
        return intRep(map(R), map(G), map(B));
    }

    private float map(float value) {
        return (value + 255) / 2;
    }

    /*
    public void check() {
    check(0);
    }
    
    public void check(int threshold) {
    if (R < threshold) {
    R = 0;
    }
    if (G < threshold) {
    G = 0;
    }
    if (B < threshold) {
    B = 0;
    }
    }
     */
    /*
    public boolean above(int threshold) {
    return above(threshold, threshold, threshold);
    }
    
     */
    public boolean above(Color_Float threshold) {
        return (R > threshold.R) && (G > threshold.G) && (B > threshold.B);
    }

    public boolean below(Color_Float threshold) {
        return (R < threshold.R) && (G < threshold.G) && (B < threshold.B);
    }

    @Override
    public void invert() {
        R = -R;
        G = -G;
        B = -B;
    }

    public Color_Short toColorInt() {
        return new Color_Short(R, G, B);
    }

    public static Color_Float[] toColorFloat(int[] intArray) {
        Color_Float[] newArray = new Color_Float[intArray.length];

        for (int i = 0; i < intArray.length; i++) {
            newArray[i] = new Color_Float(intArray[i]);
        }
        return newArray;
    }

    public int toGrayScale() {
        double average = (R + G + B) / 3.0;
        return intRep(average, average, average);
    }

    @Override
    public void clamp() {
        R = MathExtended.clamp((int) Math.abs(R), 0, 255);
        G = MathExtended.clamp((int) Math.abs(G), 0, 255);
        B = MathExtended.clamp((int) Math.abs(B), 0, 255);
    }

    /**
     * Returns the average between this Color_Float and the given Color_Float.
     * 
     * @param c
     * @return 
     */
    public Color_Float average(Color_Float c) {
        return new Color_Float((R + c.R) / 2.0, (G + c.G) / 2.0, (B + c.B) / 2.0);
    }

    /**
     * Returns true if this Color_Float's components are identical to the
     * given Color_Float's components.
     * 
     * @param c
     * @return 
     */
    public boolean equals(Color_Float c) {
        if ((R == c.R) && (G == c.G) && (B == c.B)) {
            return true;
        }
        return false;
    }

    @Override
    public String toString() {
        return "Color: [" + String.format("%.4g", R) + ", "
                + String.format("%.4g", G) + ", " + String.format("%.4g", B)
                + "]";
    }

    public static void main(String args[]) {
        Color_Float test = new Color_Float(134, 100, 100);
        System.out.println(test);
        System.out.println(test.intRep());
        System.out.println((test.intRep() >> 16) & 255);
    }
}
